package main

import (
	"fmt"
	"sync"
	"time"
)

func worker(id int) {
	fmt.Printf("Worker %d starting\n", id)
	time.Sleep(time.Second)
	fmt.Printf("Worker %d done\n", id)
}

// 生产者，只发送通道
/**
i：当前循环的编号，标识不同的生产者。
ch：一个通道，用于生产者发送数据。
&wg：等待组的指针，传给生产者以便它完成时调用 wg.Done()。
*/
func producer(id int, ch chan<- int, wg *sync.WaitGroup) {
	defer wg.Done() // 函数结束时通知等待组任务完成 任务完成，计数减 1
	for i := 0; i < 3; i++ {
		val := id*10 + i
		fmt.Printf("Producer %d sending %d\n", id, val)
		ch <- val                          // 发送数据到通道
		time.Sleep(100 * time.Millisecond) // 等待一秒
	}
}

// 消费者，只接收通道
/**
ch：通道，一个只接收（单向）整数的通道，消费者通过它接收数据
done：通道，用于通知主 goroutine 消费完成。 一个只发送（单向）空结构体的通道
*/
func consumer(ch <-chan int, done chan<- struct{}) {
	for val := range ch { // 通道关闭后循环结束
		fmt.Println("Consumer received:", val)
		time.Sleep(150 * time.Millisecond)
	}
	fmt.Println("Consumer done")
	done <- struct{}{} //发送一个空结构体值，通知主 goroutine 消费者已经完成。
}

func main() {

	// demo
	for i := 1; i <= 2; i++ {
		go worker(i) // 启动 goroutine
	}
	time.Sleep(2 * time.Second) // 等待所有 goroutine 完成
	println("All done")

	// demo2

	// 创建整型和空结构体的通道。缓冲通道，容量为5
	ch := make(chan int, 5)
	//。struct{} 是 Go 中的空结构体类型，它不占用任何内存（大小为 0）。
	// chan struct{} 表示传递空结构体的通道。
	// 由于空结构体不占用内存，发送和接收时不会传输数据，只是用来传递“信号”。
	done := make(chan struct{})

	// 声明一个等待组变量  同步原语 用来等待一组 goroutine 执行完成
	var wg sync.WaitGroup

	// 启动3个生产者 向ch通道发送数据
	for i := 1; i <= 3; i++ {
		wg.Add(1) // 增加等待计数器
		/**
		i：当前循环的编号，标识不同的生产者。
		ch：一个通道，用于生产者发送数据。
		&wg：等待组的指针，传给生产者以便它完成时调用 wg.Done()。
		*/
		go producer(i, ch, &wg) // 启动一个新的 goroutine，执行 producer 函数。
	}

	// 启动一个消费者 goroutine，从 ch 接收数据，消费完成后通过 done 通知
	go consumer(ch, done)

	// 主 goroutine 等待所有生产者完成
	wg.Wait()

	// 关闭通道，通知消费者没有更多数据
	close(ch)

	// 主 goroutine 阻塞等待消费者通过 done 通道发送完成信号
	/**
	生产者通过 WaitGroup 等待所有生产者完成。
	但是消费者是单独的 goroutine，主 goroutine 需要知道消费者什么时候完成。
	由于消费者是从通道接收数据，只有当通道关闭且数据全部消费完毕后，消费者才会退出。
	这时消费者通过向 done 通道发送一个空结构体，通知主 goroutine “我已经消费完了”。
	主 goroutine 通过 <-done 阻塞等待这个信号，确保消费者完成后才继续执行。
	*/
	<-done

	fmt.Println("All done")

}
